package context.render.shader;

import java.util.HashMap;
import math.mat3;
import math.mat4;
import math.vec2;
import math.vec2i;
import math.vec3;
import math.vec3i;
import org.lwjgl.opengl.GL20;

public class Shader
{
    
    public static final String FILE_PATH = "shaders/";
    
    private static final LoadShader LOADER = new LoadShader();
    
    
    public int mID;
    
    private ShaderVert mVert;
    
    private ShaderFrag mFrag;
    
    private HashMap<String, Integer> mMap;
    
    private UniformBuffer mUnifBuff;
    
    
    public Shader(
            String aFileNameVert, 
            String aFileNameFrag)
    {
        this.mID = GL20.glCreateProgram();
        
        this.mVert = Shader.LOADER.loadVert(aFileNameVert);
        this.mFrag = Shader.LOADER.loadFrag(aFileNameFrag);
        
        GL20.glAttachShader(this.mID, this.mVert.mID);
        GL20.glAttachShader(this.mID, this.mFrag.mID);
        GL20.glLinkProgram(this.mID);
        GL20.glValidateProgram(this.mID);
        
        this.mMap = new HashMap<>();
        
        this.mUnifBuff = new UniformBuffer();
    }
    
    
    public void uniform1i(
            String aUniformName, 
            int aData)
    {
        this.mUnifBuff.uniform1i(this.getLoc(aUniformName), aData);
    }
    
    public void uniform1d(
            String aUniformName, 
            double aData)
    {
        this.mUnifBuff.uniform1d(this.getLoc(aUniformName), aData);
    }
    
    public void uniform2i(
            String aUniformName, 
            vec2i aData)
    {
        this.mUnifBuff.uniform2i(this.getLoc(aUniformName), aData);
    }
    
    public void uniform2d(
            String aUniformName, 
            vec2 aData)
    {
        this.mUnifBuff.uniform2d(this.getLoc(aUniformName), aData);
    }
    
    public void uniform3i(
            String aUniformName, 
            vec3i aData)
    {
        this.mUnifBuff.uniform3i(this.getLoc(aUniformName), aData);
    }
    
    public void uniform3d(
            String aUniformName, 
            vec3 aData)
    {
        this.mUnifBuff.uniform3d(this.getLoc(aUniformName), aData);
    }
    
    public void uniformMat3(
            String aUniformName, 
            mat3 aData)
    {
        this.mUnifBuff.uniformMat3(this.getLoc(aUniformName), aData);
    }
    
    public void uniformMat4(
            String aUniformName, 
            mat4 aData)
    {
        this.mUnifBuff.uniformMat4(this.getLoc(aUniformName), aData);
    }
    
    public void flushUniforms()
    {
        this.mUnifBuff.flush();
    }
    
    public void onEnd()
    {
        GL20.glDetachShader(this.mID, this.mVert.mID);
        GL20.glDetachShader(this.mID, this.mFrag.mID);
        this.mVert.onEnd();
        this.mFrag.onEnd();
        GL20.glDeleteProgram(this.mID);
        this.mMap.clear();
        this.mMap = null;
    }
    
    
    private int getLoc(String aUniformName)
    {
        Integer loc = this.mMap.get(aUniformName);
        if (loc == null)
        {
            loc = GL20.glGetUniformLocation(this.mID, aUniformName);
            if (loc < 0)
            {
                System.err.println("Invalid shader uniform: " + aUniformName);
                System.exit(1);
            }
            this.mMap.put(aUniformName, loc);
        }
        return loc;
    }
    
}
